SpringBoot自定义注解+AOP处理数据字典自动翻译 您所在的位置:网站首页 mysql导出数据字典 JAVA SpringBoot自定义注解+AOP处理数据字典自动翻译

SpringBoot自定义注解+AOP处理数据字典自动翻译

2023-08-19 08:22| 来源: 网络整理| 查看: 265

最近项目中数据字典的配置使用越来越多,导致代码中有大量的数据字典翻译工作,使得代码的整洁性和维护性变差。排除掉对数据结果进行“清洗”的方案后,还是决定通过注解的方式来解决这个问题。参考了一些文章后完成了这个功能(具体实在记不清了,雷同处,海涵!)。最近有些时间顺便整理一下思路。

目标功能是这样,在返回数据的实体上通过注解标记当前字段(dict_field)对应的code值,然后通过反射读取这个字段的code和字段对应的值,然后从字典表中基于code和当前的值(key)获取对应的value,然后将value赋值在另一个字段(dict_value_field,当然也可以根据需要自行处理),返回处理后的内容。

 

1 字典表字段

这里主要是code,dict_key,dict_value这三个字段

实体对象

/* */ package org.springblade.modules.system.entity; import com.baomidou.mybatisplus.annotation.IdType; import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableLogic; import com.baomidou.mybatisplus.annotation.TableName; import com.fasterxml.jackson.databind.annotation.JsonSerialize; import com.fasterxml.jackson.databind.ser.std.ToStringSerializer; import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; import lombok.Data; import java.io.Serializable; /** * 实体类 * * @author Chill */ @Data @TableName("blade_dict") @ApiModel(value = "Dict对象", description = "Dict对象") public class Dict implements Serializable { public final static Long TOPDICT_PARENTID=0L;//父类的parentid public final static String KUAIDI_COM="kuaidi_com";//快递公司 public final static String CANCEL_REASON="cancelreason";//取消订单的原因 public final static Integer IS_SEALED_YES = 1;//封存 public final static Integer IS_SEALED_NO = 0;//没有封存 private static final long serialVersionUID = 1L; /** * 主键 */ @JsonSerialize(using = ToStringSerializer.class) @ApiModelProperty(value = "主键") @TableId(value = "id", type = IdType.ID_WORKER) private Long id; /** * 父主键 */ @JsonSerialize(using = ToStringSerializer.class) @ApiModelProperty(value = "父主键") private Long parentId; /** * 字典码 */ @ApiModelProperty(value = "字典码") private String code; /** * 字典值 */ @ApiModelProperty(value = "字典值") private String dictKey; /** * 字典名称 */ @ApiModelProperty(value = "字典名称") private String dictValue; /** * 排序 */ @ApiModelProperty(value = "排序") private Integer sort; /** * 字典备注 */ @ApiModelProperty(value = "字典备注") private String remark; /** * 是否已封存 */ @ApiModelProperty(value = "是否已封存") private Integer isSealed; /** * 是否已删除 */ @TableLogic @ApiModelProperty(value = "是否已删除") private Integer isDeleted; }

2 基本思路,步骤

2.1 新建注解DataDict,主要是用户标记实体字段对应的code。

package org.springblade.modules.focus.aspect; import io.swagger.annotations.ApiModelProperty; import java.lang.annotation.*; @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.FIELD) @Documented public @interface DataDict { @ApiModelProperty(value = "可以用于设置默认的key值") String dictKey() default ""; @ApiModelProperty(value = "需要翻译的code") String code() default ""; }

2.2 新建注解DataDictClass,这个注解主要给AOP使用,放在需要翻译的方法上,然后AOP基于这个注解做一个切面,进行数据处理。

package org.springblade.modules.focus.aspect; import java.lang.annotation.*; @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) @Documented public @interface DataDictClass { }

2.3 切面逻辑

2.3.1 切入点-基于注解dataDictClass

@Pointcut("@annotation(dataDictClass)") public void doDataDictClass(DataDictClass dataDictClass) { }

2.3.2 数据处理,先说一下当前项目的返回数据。结果全部封装在R对象中。

// // Source code recreated from a .class file by IntelliJ IDEA // (powered by Fernflower decompiler) // package org.springblade.core.tool.api; import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; import java.io.Serializable; import java.util.Optional; import org.springblade.core.tool.utils.ObjectUtil; import org.springframework.lang.Nullable; @ApiModel( description = "返回信息" ) public class R implements Serializable { private static final long serialVersionUID = 1L; @ApiModelProperty( value = "状态码", required = true ) private int code; @ApiModelProperty( value = "是否成功", required = true ) private boolean success; @ApiModelProperty("承载数据") private T data; @ApiModelProperty( value = "返回消息", required = true ) private String msg; private R(IResultCode resultCode) { this(resultCode, (Object)null, resultCode.getMessage()); } private R(IResultCode resultCode, String msg) { this(resultCode, (Object)null, msg); } private R(IResultCode resultCode, T data) { this(resultCode, data, resultCode.getMessage()); } private R(IResultCode resultCode, T data, String msg) { this(resultCode.getCode(), data, msg); } private R(int code, T data, String msg) { this.code = code; this.data = data; this.msg = msg; this.success = ResultCode.SUCCESS.code == code; } public R() { } }

这里需要处理的数据类型

2.3.2.1: 分页数据R

2.3.2.2: 普通列表R

2.3.2.3 :普通数据R

所以这里主要针对这几种数据来处理

@Around("@annotation(dataDictClass)") public Object translation(final ProceedingJoinPoint pjp, DataDictClass dataDictClass) throws Throwable { // 拿到要返回的数据--这里可以打印下看看内容 Object resultR = pjp.proceed(); if (ObjectUtil.isNull(resultR)) { return resultR; } Object obj; Object resultPage = ((R) resultR).getData(); if (resultPage instanceof IPage) { // 分页的情况 return translationPage(pjp); } Object result = ((R) resultR).getData(); result = translate(result); ((R) resultR).setData(result); // 返回处理后的数据 return resultR; }

完整代码

package org.springblade.modules.focus.aspect; import cn.hutool.core.collection.CollectionUtil; import cn.hutool.core.util.ObjectUtil; import com.baomidou.mybatisplus.core.metadata.IPage; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Pointcut; import org.springblade.core.tool.api.R; import org.springblade.modules.focus.dto.DataDictDTO; import org.springblade.modules.system.entity.Dict; import org.springframework.stereotype.Component; import java.lang.reflect.Field; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; /** * 字典注解内容翻译 */ @Aspect @Component @SuppressWarnings({"unused"}) public class DatDictAspect { // @Autowired // private DictUtils dictUtils; private Map dictInfoMap = new ConcurrentHashMap(); @Pointcut("@annotation(dataDictClass)") public void doDataDictClass(DataDictClass dataDictClass) { } @Around("@annotation(dataDictClass)") public Object translation(final ProceedingJoinPoint pjp, DataDictClass dataDictClass) throws Throwable { Object resultR = pjp.proceed(); if (ObjectUtil.isNull(resultR)) { return resultR; } Object obj; Object resultPage = ((R) resultR).getData(); if (resultPage instanceof IPage) { // 分页的情况 return translationPage(pjp); } Object result = ((R) resultR).getData(); result = translate(result); ((R) resultR).setData(result); return resultR; } public Object translationPage(final ProceedingJoinPoint pjp) throws Throwable { Object resultR = pjp.proceed(); if (resultR == null) { return resultR; } Object resultPage = ((R) resultR).getData(); IPage page = (IPage) resultPage; Object result = ((IPage) resultPage).getRecords(); result = translate(result); page.setRecords((List) result); ((R) resultR).setData(page); return resultR; } private Object translate(Object result) { Object obj; if (result instanceof List || result instanceof ArrayList) { List olist = ((List) result); if (olist.size() == 0) { return result; } obj = olist.get(0); } else { obj = result; } // 获取所有配置注解的code 和实体字段 List dictParams = getDictMapping(obj.getClass()); if (dictParams.size() == 0) { return result; } //根据code 获取对应的字典内容 // List dictInfos = dictUtils.getDicts(getCodes(dictParams)); List dictInfos = getDictTest(); if (dictInfos == null && dictInfos.size() == 0) { return result; } //先把字典值转成map for (Dict dict : dictInfos) { dictInfoMap.put(dict.getCode() + "_" + dict.getDictKey(), dict.getDictValue()); } if (result instanceof List || result instanceof ArrayList) { for (Object entity : (List) result) { assign(entity, dictParams, dictInfoMap); } } else { assign(result, dictParams, dictInfoMap); } return result; } /** * 测试用---实际中最好把所有的数据封装在redis中 * @return */ private List getDictTest(){ List dicts = new ArrayList(); Dict dict1 = new Dict(); dict1.setCode("channelCode"); dict1.setDictKey("20"); dict1.setDictValue("版本20"); dicts.add(dict1); return dicts; } /** * 单个设置值 * 这里的逻辑是获取配置注解的字段名称---比如 dict * 然后将从字典拿到的值赋值给另一个字段---dictValue * 当然可以根据具体情况去处理 * * @param entity * @param dictParams * @param dictInfoMap */ public void assign(Object entity, List dictParams, Map dictInfoMap) { for (DataDictDTO dictParam : dictParams) { String code = dictParam.getCode(); String dictName = dictParam.getFieldName(); try { Class c = entity.getClass(); if (c != null) { Field f = c.getDeclaredField(dictName); f.setAccessible(true); Object preValue = f.get(entity); if (ObjectUtil.isNotNull(preValue)) { // 需要赋值的字段 Field fvalue = c.getDeclaredField(dictName + "Value"); fvalue.setAccessible(true); fvalue.set(entity, dictInfoMap.getOrDefault(code + "_" + preValue.toString(), preValue.toString())); } } } catch (Exception e) { e.printStackTrace(); } } } /** * 获取实体中所有需要翻译的code * @param dataDictDTOS * @return */ private List getCodes(List dataDictDTOS) { List codes = new ArrayList(); if (CollectionUtil.isEmpty(dataDictDTOS)) { return codes; } for (DataDictDTO dataDictDTO : dataDictDTOS) { codes.add(dataDictDTO.getCode()); } return codes; } /** * 获取实体中配置的code 和对应的字段内容 * @param cla * @return */ private List getDictMapping(Class cla) { Field[] fields = cla.getDeclaredFields(); List list = new ArrayList(); DataDictDTO dataDictDTO; DataDict dataDict; for (Field field : fields) { if (field.isAnnotationPresent(DataDict.class)) { dataDict = field.getAnnotation(DataDict.class); dataDictDTO = new DataDictDTO(dataDict.code(), field.getName()); list.add(dataDictDTO); } } return list; } }

3 测试

3.1 实体添加注解

package org.springblade.modules.thirdparty.vo; import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; import lombok.Data; import org.springblade.modules.focus.aspect.DataDict; import javax.validation.constraints.NotNull; @Data @ApiModel("测试对象") public class SharpernerVO { @NotNull @ApiModelProperty("id") private String serId; @NotNull @ApiModelProperty("需要翻译的字段") @DataDict(code = "channelCode") private String dictKey; @NotNull @ApiModelProperty("翻译后的值放在这个字段") private String dictKeyValue; public SharpernerVO() { } public SharpernerVO(@NotNull String dictKey, @NotNull String dictKeyValue) { this.dictKey = "20"; this.dictKeyValue = dictKeyValue; } }

3.2 方法添加注解

@GetMapping(value = "/dict") @ApiOperationSupport(order = 2) @ApiOperation(value = "话费提交接口", notes = "话费提交接口") @DataDictClass public R dict() { return R.data(sharpenerdaservice.getDicts()); } 3.3 测试结果 { "code": 200, "success": true, "data": [ { "serId": "", "dictKey": "20", "dictKeyValue": "版本20" } ], "msg": "操作成功" }

已经将字典配置的value返回过来。

 

 

 



【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有